package gxprod.androidai.fetch;

import gxprod.androidai.BubbleAdapter;
import gxprod.androidai.DataCollection;
import gxprod.androidai.Operation;
import gxprod.androidai.functions.Function;
import gxprod.androidai.functions.WeatherFunction;
import gxprod.androidai.regex.MatchResult;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;

/**
 * Retrieves the most recent weather information, then preforms WeatherFunction.execute, if needed.
 * @author Josh Maleszewski
 */
public class FetchWeather implements Operation.ParmedRunnable {
	public WeatherFunction output;
	public boolean reoutput = true;
	public String zipCode;
	public FetchWeather(WeatherFunction wf, String zipCode){
		output = wf;
		this.zipCode = zipCode;
	}
	public boolean run(MatchResult result, BubbleAdapter adapter, Object o) {

		if(adapter != null) {
			adapter.output("Let me check...");
			adapter.postUpdate();
		}
		ArrayList<WeatherCondition> conditions = new ArrayList<WeatherCondition>();
		try {
			BufferedReader reader = Function.fetch("http://free.worldweatheronline.com/feed/weather.ashx?q=" + zipCode + "&format=csv&num_of_days=5&key=514398cff4204801122702");

			if(reader == null) {
				adapter.output("I was unable to get the weather information. You may not have an internet conection");
				return false;
			}


			WeatherCondition WC = new WeatherCondition();
			WeatherCondition WC2 = new WeatherCondition();

			String line = reader.readLine();
			String[] parts;			

			while(line.startsWith("#"))
				line = reader.readLine();

			parts = line.split(",");	

			if(parts.length == 1) {
				adapter.output("I was unable to get the weather: " + parts[0]);
				return false;				
			}

			/* #observation_time, temp_C, weatherCode, weatherIconUrl, weatherDesc, (5)
			 windspeedMiles, windspeedKmph, winddirDegree, winddir16Point, precipMM, 
			 humidity, visibility, pressure, cloudcover      */

			WC2.Date 			= new Date();
			WC2.TempC			= Integer.parseInt(parts[1]);
			WC2.WeatherCode		= Integer.parseInt(parts[2]);
			////WC.Icon			= parts[3];
			WC2.Descript		= parts[4];
			WC2.WindSpeedMi		= Float.parseFloat(parts[5]);
			WC2.WindSpeedKm		= Float.parseFloat(parts[6]);
			WC2.WindDirrect		= Integer.parseInt(parts[7]);
			WC2.WindDirect16 	= parts[8];
			WC2.WindDirrect		= Float.parseFloat(parts[9]);
			WC2.Humidity		= Integer.parseInt(parts[10]);
			WC2.Visibility		= Integer.parseInt(parts[11]);
			WC2.Preasure		= Integer.parseInt(parts[12]);
			WC2.CloudCover		= Float.parseFloat(parts[13]);

			conditions.add(WC2);

			/*      #date, tempMaxC, tempMaxF,  tempMinC, tempMinF, windspeedMiles, windspeedKmph, winddirDegree,
			 winddir16Point, weatherCode, weatherIconUrl, weatherDesc, precipMM      */

			while ((line = reader.readLine()) != null) {
				parts = line.split(",");					
				WC = new WeatherCondition();

				try {
					WC.Date 				= new Date(Date.parse(parts[0]));
				} catch(Exception ex) {
					Date prev = (Date)conditions.get(conditions.size() - 1).Date.clone();
					prev.setDate(prev.getDate() + 1);
					WC.Date = prev;
				}
				WC.TempHighC 			= Integer.parseInt(parts[1]);
				WC.TempHighF 			= Integer.parseInt(parts[2]);
				WC.TempLowC 			= Integer.parseInt(parts[3]);
				WC.TempLowF 			= Integer.parseInt(parts[4]);
				WC.WindSpeedMi			= Float.parseFloat(parts[5]);
				WC.WindSpeedKm			= Float.parseFloat(parts[6]);
				WC.WindDirrect			= Float.parseFloat(parts[7]);
				WC.WindDirect16 		= parts[8];
				WC.WeatherCode			= Integer.parseInt(parts[9]);
				WC.Descript				= parts[11];
				WC.PrecipitationChance	= Float.parseFloat(parts[12]);

				conditions.add(WC);
			}

			DataCollection.setWeather(new WeatherModel(WC2, conditions));
			if(reoutput)
				output.execute(result, adapter);
			
		} catch (IOException e) {
			e.printStackTrace();
			adapter.output("I was unable to get the weather information.");
			return false;
		}


		return true;
	}
	public boolean cancel() { 
		reoutput = false; 
		return true; 
	}

	public class WeatherCondition {

		public int TempC;
		public int TempHighC;
		public int TempHighF;
		public int TempLowC;
		public int TempLowF;
		public float WindDirrect;
		public String WindDirect16;
		public int WeatherCode;
		public int Humidity;
		public int Preasure;
		public int Visibility;
		public float PrecipitationChance;
		public float CloudCover;
		public float WindSpeedMi;
		public float WindSpeedKm;
		public Date Date;
		public String Descript;

		public String getDescription(boolean metric) {
			StringBuffer sb = new StringBuffer();
			if(Descript != null) {
				sb.append(Descript);
				sb.append(" with a ");
			}
			sb.append("high of ");
			sb.append((metric)? TempHighC:TempHighF);
			sb.append(" and low of ");
			sb.append((metric)? TempHighC:TempHighF);
			sb.append(" degrees");
			return sb.toString();
		}

		public boolean willItRain() { return Descript.contains("rain") | Descript.contains("shower"); }
		public boolean willItSnow() { return Descript.contains("snow"); }

		public int getLow() { return (DataCollection.usesMetricSystem()) ? TempLowC : TempLowF; }
		public int getHigh() { return (DataCollection.usesMetricSystem()) ? TempHighC : TempHighF; }
		public int getTemp() { return (DataCollection.usesMetricSystem()) ? TempC : 
			celsiusToFahrenheit(TempC);
		}
	}

	public class WeatherModel {

		public WeatherCondition Now;
		public ArrayList<WeatherCondition> Later;

		public WeatherModel(WeatherCondition now, ArrayList<WeatherCondition> later) {
			Now = now;
			Later = later;
		}

		public String getTodayDescript() {
			boolean metric = DataCollection.usesMetricSystem();
			StringBuffer sb = new StringBuffer();			
			WeatherCondition wc = Later.get(1);

			sb.append(Now.Descript);
			sb.append(" and it's about ");
			sb.append((metric) ? Now.TempC : celsiusToFahrenheit(Now.TempC));
			sb.append(" degrees");
			if(metric) sb.append(" C");
			sb.append(", high: ");
			sb.append(wc.getHigh());
			sb.append(", Low: ");
			sb.append(wc.getLow());

			return sb.toString();
		}
	}

	public static int celsiusToFahrenheit(int tempInCelsius) { return ((tempInCelsius * 9/5) + 32); }
	public static int fahrenheitToCelsius(int tempInFahrenheit) { return ((tempInFahrenheit - 32) * 5/9); }
}
